<?php

namespace Tbf\Bundle\PackageBundle\TreePackage;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Tbf\Component\GiveName\GiveNameFunction;
use Tbf\Bundle\PackageBundle\Annotation\Annotation;
use Tbf\Bundle\PackageBundle\ClassGenerator\EntityDefine;
use Tbf\Bundle\PackageBundle\ClassGenerator\PhpProperty;
use Tbf\Bundle\PackageBundle\PackageInterface;
use Tbf\Bundle\PackageBundle\Type\Field;
use Tbf\Bundle\PackageBundle\Type\GroupManager;

abstract class TreePackage implements PackageInterface
{
    protected function getTableName()
    {
        $p1 = $this->getNamespace();
        $p1 = ltrim($p1,'\\');
        $p1 = strtolower($p1);
        $p1 = str_replace(array('\\','bundle'),array('_',''),$p1);

        return $p1.'_'.$this->getLowerName();
    }
    protected function getName()
    {
        $class_name = $this->getReflectionSelfClass()->getShortName();
        $pos = strrpos($class_name,'Package');
        if ($pos===false) {
            return '';
        }
        $ret = substr($class_name,0,$pos);

        return $ret;
    }
    protected function getEntityDir()
    {
        $file_path = $this->getReflectionSelfClass()->getFileName();

        return dirname($file_path).'/../Entity';
    }
    protected function getNamespace()
    {
        $class_namespace = $this->getReflectionSelfClass()->getNamespaceName();
        $pos = strrpos($class_namespace,'\\Package');
        if ($pos===false) {
            return '';
        }
        $ret = substr($class_namespace,0,$pos);

        return $ret;
    }
    protected function getReflectionSelfClass()
    {
        return new \ReflectionObject($this);
    }
    protected function getLowerName()
    {
        return GiveNameFunction::to($this->getName(),'GiveName','give_name');
    }
    protected function getControllerServiceId()
    {
        $p1 = $this->getNamespace();
        $p1 = ltrim($p1,'\\');
        $p1 = strtolower($p1);
        $p1 = str_replace(array('\\','bundle'),array('_',''),$p1);
        $p2 = $this->getLowerName().'_controller';

        return $p1.'.'.$p2;
    }
    protected function getEntityClassName()
    {
        return $this->getNamespace().'\\Entity\\'.$this->getName();
    }
    protected function getEntityFilePath()
    {
        return $this->getEntityDir().'/'.$this->getName().'.php';
    }
    protected function getClosureEntityClassName()
    {
        return $this->getNamespace().'\\Entity\\'.$this->getName().'Closure';
    }
    protected function getClosureEntityFilePath()
    {
        return $this->getEntityDir().'/'.$this->getName().'Closure.php';
    }
    protected function getDrName()
    {
        return $this->getEntityClassName();
    }
    protected function getClosureTableName()
    {
        return $this->getTableName().'_closure';
    }
    protected function getMainTableName()
    {
        return $this->getTableName();
    }
    protected function getRoutePrefix()
    {
        return '/'.$this->getLowerName();
    }
    /**
     * @return \Tbf\Bundle\PackageBundle\Type\Field[]
     */
    public function getFieldConfig()
    {
        return array(
            Field::create('id')
                ->setGroupManager(
                    GroupManager::create()
                        ->excludeNotExistGroup()
                        ->includeGroupList(array('list','update'))
                )
                ->addAnnotation(new Annotation('\Doctrine\ORM\Mapping\Column', array('name' => 'id', 'type' => 'integer')))
                ->addAnnotation(new Annotation('\Doctrine\ORM\Mapping\Id'))
                ->addAnnotation(new Annotation('\Doctrine\ORM\Mapping\GeneratedValue'))
            ,
            Field::create('pid')
                ->setGroupManager(
                    GroupManager::create()
                        ->excludeNotExistGroup()
                        ->includeGroupList(array('list','update','create'))
                )->NotOnEntity(),
            Field::create('parent')
                ->setGroupManager(
                    GroupManager::create()->excludeNotExistGroup()
                )
                ->addAnnotation(new Annotation('\Gedmo\Mapping\Annotation\TreeParent'))
                ->addAnnotation(new Annotation('\Doctrine\ORM\Mapping\ManyToOne', array('targetEntity' => $this->getEntityClassName(), 'inversedBy' => 'children')))
                ->addAnnotation(new Annotation('\Doctrine\ORM\Mapping\JoinColumn', array('name' => "pid", 'referencedColumnName' => "id", 'onDelete' => "CASCADE")))
            ,
            Field::create('children')
                ->setGroupManager(
                    GroupManager::create()->excludeNotExistGroup()
                )
                ->addAnnotation(new Annotation('\Doctrine\ORM\Mapping\OneToMany', array('targetEntity' =>  $this->getEntityClassName(), 'mappedBy' => 'parent')))
        );
    }
    public function getFieldListByGroup($group)
    {
        $output = array();
        foreach ($this->getFieldConfig() as $field) {
            if ($field->getGroupManager()->match($group)) {
                $output[] = $field->getName();
            }
        }

        return $output;
    }
    public function getRouteConfig()
    {
        return array(
            'list'=>'@listAction',
            'create'=>'@createAction',
            'update'=>'@updateAction',
            'delete'=>'@deleteAction',
        );
    }
    public function getRouteCollection()
    {
        $collection = new RouteCollection();
        foreach ($this->getRouteConfig() as $name=>$config_method) {
            if (strpos($config_method,'@')===0) {
                $method = $this->getControllerServiceId().':'.substr($config_method,1);
            } else {
                $method = $config_method;
            }
            $collection->add($this->getLowerName().'_'.$name, new Route('/'.$name, array(
                '_controller' => $method,
            )));
        }
        $collection->addPrefix($this->getRoutePrefix());

        return $collection;
    }
    public function compilerPass(ContainerBuilder $container)
    {
        $definition = new DefinitionDecorator('tbf.container_aware');
        $definition->setClass('Tbf\Bundle\PackageBundle\TreePackage\TreeController')
            ->addArgument(array(
                'dr_name'=>$this->getDrName(),
                'create_field_list'=>$this->getFieldListByGroup('create'),
                'update_field_list'=>$this->getFieldListByGroup('update'),
                'list_field_list'=>$this->getFieldListByGroup('list'),
                'from_array_field_list'=>$this->getFieldListByGroup('from_array'),
                'entity_class_name'=>$this->getEntityClassName(),
            ));
        $container->setDefinition($this->getControllerServiceId(),
            $definition);
    }

    /**
     * @return array|\Tbf\Bundle\PackageBundle\ClassGenerator\EntityDefine[]
     */
    public function getEntitiesDefine()
    {
        $main_ed = EntityDefine::create($this->getEntityClassName())
            ->setFilePath($this->getEntityFilePath())
            ->setParentClassName('Tbf\Bundle\PackageBundle\TreePackage\AbstractTreeEntity')
            ->addAnnotation(new Annotation('\Doctrine\ORM\Mapping\Table', array('name' => $this->getMainTableName())))
            ->addAnnotation(new Annotation('\Gedmo\Mapping\Annotation\Tree', array('type' => 'closure')))
            ->addAnnotation(new Annotation('\Gedmo\Mapping\Annotation\TreeClosure', array('class' => $this->getClosureEntityClassName())))
            ->addAnnotation(new Annotation('\Doctrine\ORM\Mapping\Entity', array('repositoryClass' => 'Gedmo\Tree\Entity\Repository\ClosureTreeRepository')));

        foreach ($this->getFieldConfig() as $field) {
            if (!$field->isOnEntity()) {
                continue;
            }
            $property = PhpProperty::create($field->getName())
                  ->setVisibility('protected')
                  ->setAnnotationList($field->getAnnotationList());
            if ($field->getDefault()!==null) {
                $property->setDefaultValue($field->getDefault());
            }
            $main_ed->setProperty($property);
        }
        $main_ed->addGetterSetterMethod();

        $closure_ed = EntityDefine::create($this->getClosureEntityClassName())
            ->setFilePath($this->getClosureEntityFilePath())
            ->setParentClassName('\Gedmo\Tree\Entity\MappedSuperclass\AbstractClosure')
            ->addAnnotation(new Annotation('\Doctrine\ORM\Mapping\Table', array('name' => $this->getClosureTableName())))
            ->addAnnotation(new Annotation('\Doctrine\ORM\Mapping\Entity'));

        return array(
            'main'=>$main_ed,
            'closure'=>$closure_ed);
    }
}
